#!/bin/sh
# Shadowsock Running Web Manager
SS_WEB_BIN="$0"
SS_WEB_BIN_NAME=${SS_WEB_BIN##*/}

SHADOWSOCKS_CONFIG='/etc/config/shadowsocks'
REDSOCKS2_CONFIG='/etc/config/redsocks2'
SERVER_LIST_FILE='/etc/shadowsocks.list'
NS_SERVER='114.114.114.114'
UPDATE_URL='http://leenchan.ddns.info:88/cgi-bin/shadowsocks.list'
BREAK_CHECK_RETRY='3'
BREAK_CHECK_TIMEOUT='5'
# BREAK_CHECK_URL="http://icanhazip.com?$(date +"%Y%m%d%H%M%S")"
BREAK_CHECK_URL="https://www.youtube.com"


web_get_server_now() {
	running_check && running_details && {
		echo "$RUNNING:$server_now"
	}
}

web_get_auto_time() {
	auto_switch_cron_check && {
		web_data=$(echo "$CRON_BIN" | awk '{print $1}')
		echo ${web_data##*/}
	}
}

web_set_auto_switch() {
	ERR=''
	MSG=''
	[ -z "$1" ] && ERR=$([ -z "$ERR" ]||echo "${ERR}\n")"No server selected for Auto Switch."
	if [ -z "$2" ]; then
		ERR=$([ -z "$ERR" ] || echo "${ERR}\n")"Check time has not set."
	else
		[ "$2" -gt "0" ] || ERR=$([ -z "$ERR" ] || echo "${ERR}\n")"Check Time have to be an integer."
	fi
	[ -z "$ERR" ] || {
		echo -e "$ERR" && return 1
	}
	if running_check; then
		[ -z "$3" ] || {
			running_details
			[ "$3" = "$proxy_type_now" ] || {
				client_restart "$3" || ERR="Fail to set check time."
			}
		}
	else
		switch_auto "$3" || ERR="Fail to enable Auto Switch."
	fi
	if [ -z "$ERR" ]; then
		if auto_switch_cron_check; then
			MSG="Set check time to $2 minutes."
		else
			MSG="Auto Switch has been enable."
		fi
		auto_switch_cron_set "$2"
		echo "$MSG" && return 0
	else
		echo "$ERR" && return 1
	fi
}

running_check() {
	RUNNING=''
	for i in ss-redir ss-local redsocks2;
	do
		pidof $i &>/dev/null && RUNNING=$i && break
	done
	[ -z "$RUNNING" ] && return 1
	return 0
}

running_details() {
	find_it=$( ps -w | grep -i "$RUNNING" | grep -v 'grep' )
	[ -z "$find_it" ] && return 1
	process=$( echo $find_it | awk '{print $5}' )
	option=$( echo $find_it | awk '{for(i=6;i<=NF;i++) print $i}' )
	case $RUNNING in
		ss-redir)
			server_now=$( echo $option | awk '{ for( i=1; i<=NF; i++ ) if ( $i=="-s" ) { print $(i+1) } }' )
			proxy_type_now='shadowsocks'
			;;
		ss-local)
			server_now=$( echo $option | awk '{ for( i=1; i<=NF; i++ ) if ( $i=="-s" ) { print $(i+1) } }' )
			proxy_type_now='shadowsocks'
			;;
		redsocks2)
			REDSOCKS2_CONF=$( echo $option | awk '{ for( i=1; i<=NF; i++ ) if ( $i=="-c" ) { print $(i+1) } }' )
			line_no=$(grep -i -n 'shadowsocks' "$REDSOCKS2_CONF")
			line_no=${line_no%%:*}
			[ -z "$line_no" ] && return 1
			server_now=$(grep -Ei '[	]?ip =' "$REDSOCKS2_CONF")
			server_now=$(echo "${server_now##*=}"|grep -Eo '[^ ][^;]+')
			proxy_type_now='redsocks2'
			;;
	esac
	[ -z "$server_now" ] && return 1
	return 0
}

resolve_ip() {
	[ -z "$1" ] && return 1
	server_ips=$( nslookup "$1" "$NS_SERVER" | awk 'NR>=5{print $3}' )
	# if ( command -v resolveip &>/dev/null ); then
	# 	server_ips=$( resolveip -4 -t 10 "$1" )
	# else
	# 	server_ips=$( nslookup "$1" "$NS_SERVER" | awk 'NR>=5{print $3}' )
	# fi
	if [ -z "$server_ips" ]; then
		return 1
	else
		echo -e "$server_ips"
		return 0
	fi
}

get_location() {
	[ -z "$1" ] && return 1
	HTTP_RETURN=$(wget -qO - "http://www.ip.cn/index.php?ip=$1" | grep -Eo "GeoIP[^<]+")
	echo "${HTTP_RETURN##*: }"
}

set_flag() {
FLAGS="Algeria:dz
Algeria:dz
Angola:ao
Benin:bj
Botswana:bw
Burkina Faso:bf
Burundi:bi
Cameroon:cm
Cape Verde:cv
Central African Republic:cf
Chad:td
Comoros:km
Congo:cg
Congo, The Democratic Republic of the:cd
Côte d'Ivoire:ci
Djibouti:dj
Egypt:eg
Equatorial Guinea:gq
Eritrea:er
Ethiopia:et
Gabon:ga
Gambia:gm
Ghana:gh
Guinea:gn
Guinea-Bissau:gw
Kenya:ke
Lesotho:ls
Liberia:lr
Libya:ly
Madagascar:mg
Malawi:mw
Mali:ml
Mauritania:mr
Mauritius:mu
Mayotte:yt
Morocco:ma
Mozambique:mz
Namibia:na
Niger:ne
Nigeria:ng
Rwanda:rw
Réunion:re
Saint Helena:sh
Sao Tome and Principe:st
Senegal:sn
Seychelles:sc
Sierra Leone:sl
Somalia:so
South Africa:za
South Sudan:ss
Sudan:sd
Swaziland:sz
Tanzania:tz
Togo:tg
Tunisia:tn
Uganda:ug
Western Sahara:eh
Zambia:zm
Zimbabwe:zw
Anguilla:ai
Antigua and Barbuda:ag
Argentina:ar
Aruba:aw
Bahamas:bs
Barbados:bb
Belize:bz
Bermuda:bm
Bolivia, Plurinational State of:bo
Brazil:br
Canada:ca
Cayman Islands:ky
Chile:cl
Colombia:co
Costa Rica:cr
Cuba:cu
Curaçao:cw
Dominica:dm
Dominican Republic:do
Ecuador:ec
El Salvador:sv
Falkland Islands (Malvinas):fk
French Guiana:gf
Greenland:gl
Grenada:gd
Guadeloupe:gp
Guatemala:gt
Guyana:gy
Haiti:ht
Honduras:hn
Jamaica:jm
Martinique:mq
Mexico:mx
Montserrat:ms
Netherlands Antilles:an
Nicaragua:ni
Panama:pa
Paraguay:py
Peru:pe
Puerto Rico:pr
Saint Kitts and Nevis:kn
Saint Lucia:lc
Saint Pierre and Miquelon:pm
Saint Vincent and the Grenadines:vc
Sint Maarten:sx
Suriname:sr
Trinidad and Tobago:tt
Turks and Caicos Islands:tc
United States:us
Uruguay:uy
Venezuela, Bolivarian Republic of:ve
Virgin Islands, British:vg
Virgin Islands, U.S.:vi
Afghanistan:af
Armenia:am
Azerbaijan:az
Bahrain:bh
Bangladesh:bd
Bhutan:bt
Brunei Darussalam:bn
Cambodia:kh
China:cn
Cyprus:cy
Georgia:ge
Hong Kong:hk
India:in
Indonesia:id
Iran, Islamic Republic of:ir
Iraq:iq
Israel:il
Japan:jp
Jordan:jo
Kazakhstan:kz
Korea, Democratic People's Republic of:kp
Korea, Republic of:kr
Kuwait:kw
Kyrgyzstan:kg
Lao People's Democratic Republic:la
Lebanon:lb
Macao:mo
Malaysia:my
Maldives:mv
Mongolia:mn
Myanmar:mm
Nepal:np
Oman:om
Pakistan:pk
Palestinian Territory, Occupied:ps
Philippines:ph
Qatar:qa
Saudi Arabia:sa
Singapore:sg
Sri Lanka:lk
Syrian Arab Republic:sy
Taiwan, Province of China:tw
Tajikistan:tj
Thailand:th
Timor-Leste:tl
Turkey:tr
Turkmenistan:tm
United Arab Emirates:ae
Uzbekistan:uz
Viet Nam:vn
Yemen:ye
American Samoa:as
Australia:au
Cook Islands:ck
Fiji:fj
French Polynesia:pf
Guam:gu
Kiribati:ki
Marshall Islands:mh
Micronesia, Federated States of:fm
Nauru:nr
New Caledonia:nc
New Zealand:nz
Niue:nu
Norfolk Island:nf
Northern Mariana Islands:mp
Palau:pw
Papua New Guinea:pg
Pitcairn:pn
Samoa:ws
Solomon Islands:sb
Tokelau:tk
Tonga:to
Tuvalu:tv
Vanuatu:vu
Wallis and Futuna:wf
Albania:al
Andorra:ad
Austria:at
Belarus:by
Belgium:be
Bosnia and Herzegovina:ba
Bulgaria:bg
Croatia:hr
Czech Republic:cz
Denmark:dk
Estonia:ee
Faroe Islands:fo
Finland:fi
France:fr
Germany:de
Gibraltar:gi
Greece:gr
Holy See (Vatican City State):va
Hungary:hu
Iceland:is
Ireland:ie
Italy:it
Kosovo:xk
Latvia:lv
Liechtenstein:li
Lithuania:lt
Luxembourg:lu
Macedonia, The Former Yugoslav Republic of:mk
Malta:mt
Moldova, Republic of:md
Monaco:mc
Montenegro:me
Netherlands:nl
Norway:no
Poland:pl
Portugal:pt
Romania:ro
Russian Federation:ru
San Marino:sm
Serbia:rs
Slovakia:sk
Slovenia:si
Spain:es
Sweden:se
Switzerland:ch
Ukraine:ua
United Kingdom:gb
Bouvet Island:bv
British Indian Ocean Territory:io
Canary Islands:ic
Catalonia:catalonia
England:england
European Union:eu
French Southern Territories:tf
Guernsey:gg
Heard Island and McDonald Islands:hm
Isle of Man:im
Jersey:je
Kurdistan:kurdistan
Scotland:scotland
Somaliland:somaliland
South Georgia and the South Sandwich Islands:gs
Tibet:tibet
United States Minor Outlying Islands:um
Wales:wales
Zanzibar:zanzibar"
}

get_flag() {
	[ -z "$1" ] && return 1
	[ -z "$FLAGS" ] && set_flag
	FLAG_ST=$(echo "$FLAGS"|grep -i -m 1 "$1")
	if [ -z "$FLAG_ST" ]; then
		return 1
	else
		echo ${FLAG_ST##*:}
	fi
}

iptables_add() {
	[ -z "$1" ] && return 1
	ip=$( echo "$1" | grep -o '\([0-9]\{1,3\}\\.\)\{3\}[0-9]\{1,3\}' )
	[ -z "$ip" ] && return 1
	iptables_match=$( iptables-save -t nat | grep "$1" )
	[ -z "$iptables_match" ] && return 1
	iptables -t nat -A SHADOWSOCKS_WAN -d "$ip" -j RETURN
}

server_add() {
	# SERVER:  $1 Host / $2 Port / $3 Password / $4 Method / $5 Enable Location: 1 / $6 Auto Switch: 1
	ERR=''
	[ -z "$1" ] && ERR=$([ -z "$ERR" ]||echo "${ERR}\n")"Server Host is required."
	[ -z "$2" ] && ERR=$([ -z "$ERR" ]||echo "${ERR}\n")"Server Port is required."
	[ -z "$3" ] && ERR=$([ -z "$ERR" ]||echo "${ERR}\n")"Server Password is required."
	[ -z "$4" ] && ERR=$([ -z "$ERR" ]||echo "${ERR}\n")"Server Method is required."
	[ -z "$ERR" ] || {
		echo -e "$ERR" && return 1
	}
	SERVER_HOST=''
	for LINE in $(echo "$1" | tr -d ' ' | tr ',' ' ')
	do
		if [ $( echo "$LINE" | grep -Ex '([0-9]{1,3}\.){3}[0-9]{1,3}') ]; then
			SERVER_HOST=$([ -z "$SERVER_HOST" ]||echo "$SERVER_HOST\n")"|$LINE"
		elif [ $( echo "$LINE" | grep -Ex '([0-9a-zA-Z-]+\.)+[a-zA-Z]{2,5}' ) ]; then
			RS_IPS=$(resolve_ip "$LINE")
			[ -z "$RS_IPS" ] || {
				RS_IPS=$(echo -e "$RS_IPS"|sed "s/\(.*\)/$LINE|\1/")
				SERVER_HOST=$([ -z "$SERVER_HOST" ]||echo "$SERVER_HOST\n")"$RS_IPS"
			}
		fi
	done
	[ -z "$SERVER_HOST" ] && ERR=$([ -z "$ERR" ]||echo "$ERR\n")"Invalid server host."
	SERVER_PORT="$2"
	[ "$SERVER_PORT" -gt 0 -a "$SERVER_PORT" -lt 65535 ] || ERR=$([ -z "$ERR" ]||echo "$ERR\n")"$SERVER_PORT is not a valid port ( 0~65535 )."
	SERVER_PASSWORD="$3"
	SERVER_PASSWORD="${SERVER_PASSWORD//%23/#}"; SERVER_PASSWORD="${SERVER_PASSWORD//%26/&}"; SERVER_PASSWORD="${SERVER_PASSWORD//%28/(}"; SERVER_PASSWORD="${SERVER_PASSWORD//%29/)}"; SERVER_PASSWORD="${SERVER_PASSWORD//%40/@}"; SERVER_PASSWORD="${SERVER_PASSWORD//%3A/:}"; SERVER_PASSWORD="${SERVER_PASSWORD//%3B/;}"
	SERVER_METHOD=''
	shadowsocks_methods='table rc4 rc4-md5 aes-128-cfb aes-192-cfb aes-256-cfb bf-cfb camellia-128-cfb camellia-192-cfb camellia-256-cfb cast5-cfb des-cfb idea-cfb rc2-cfb seed-cfb salsa20 chacha20'
	for LINE in $shadowsocks_methods
	do
		[ "$LINE" = "$4" ] && SERVER_METHOD="$4"
	done
	[ -z "$SERVER_METHOD" ] && ERR=$([ -z "$ERR" ]||echo "$ERR\n")"$4 is not a valid server method."
	SERVER_LIST_FILE_tmp=$(cat "$SERVER_LIST_FILE")
	for LINE in $(echo -e "$SERVER_HOST"|grep -v '^|'|sed 's/|.*//'|sort -u)
	do
		SERVER_LIST_FILE_tmp=$(echo -e "$SERVER_LIST_FILE_tmp"|grep -v "$LINE")
	done
	for LINE in $(echo -e "$SERVER_HOST"|grep '^|'|sed 's/|//'|sort -u)
	do
		SERVER_LIST_FILE_tmp=$(echo -e "$SERVER_LIST_FILE_tmp"|grep -v "$LINE")
	done
	if [ -z "$ERR" ]; then
		SERVER_LIST_FILE_add=''
		for LINE in $(echo -e "$SERVER_HOST")
		do
			if [ "$5" = '1' ]; then
				SERVER_LOCATION="$(get_location ${LINE##*|})"
				NATION_ST=$(get_flag "${SERVER_LOCATION##*, }")
				SERVER_LOCATION="$SERVER_LOCATION:$NATION_ST"
			else
				SERVER_LOCATION=''
			fi
			SERVER_LIST_FILE_add=$([ -z "$SERVER_LIST_FILE_add" ]||echo "$SERVER_LIST_FILE_add\n")"$LINE|$SERVER_PORT|$SERVER_PASSWORD|$SERVER_METHOD|$SERVER_LOCATION|${6:-0}"
		done
		echo -e "$([ -z "$SERVER_LIST_FILE_tmp" ] || echo "$SERVER_LIST_FILE_tmp\n")$([ -z "$SERVER_LIST_FILE_add" ] || echo "$SERVER_LIST_FILE_add")">"$SERVER_LIST_FILE"
		echo -e "Added shadowsocks servers:\n$([ -z "$SERVER_LIST_FILE_add" ] || echo "$SERVER_LIST_FILE_add")"
		return 0
	else
		echo "$ERR"
		return 1
	fi
}

server_remove() {
	[ -z "$1" ] && return 1
	[ -f "$SERVER_LIST_FILE" ] || {
		echo "Could not find $SERVER_LIST_FILE." && return 1
	}
	DATA_TMP=$(cat "$SERVER_LIST_FILE")
	DATA_CEHCK=$DATA_TMP
	for LINE in $(echo $1|tr ',' ' ');
	do
		DATA_CEHCK=$(echo -e "$DATA_CEHCK"|grep -v "$LINE")
	done
	if [ "$DATA_CEHCK" = "$DATA_TMP" ]; then
		echo "$1 was not in the server list."
		return 1
	else
		echo -e "$DATA_CEHCK">"$SERVER_LIST_FILE"
		# [ -z "$DATA_CEHCK" ] && auto_switch_cron_remove
		echo -e "Removed server:\n$1"
		return 0
	fi
}

server_info() {
	[ -z "$1" ] && return 1
	grep -m 1 "$1" "$SERVER_LIST_FILE"
}

server_update() {
	if [ -z "$1" ]; then
		echo "Please input your update password!"
		return 1
	else
		URL_RETURN=$( wget -qO - --tries=2  --timeout=2 "$UPDATE_URL?password=${1}" )
		[ -z "$URL_RETURN" ] && {
			echo "Failed to connecting update server."
			return 1
		}
		if [ "$URL_RETURN" = '0' ]; then
			echo "Password is invalid."
			return 1
		else
			MD5=$( echo "$URL_RETURN" | grep -x '[0-9a-z]\{32\}' )
			[ -z "$MD5" ] && {
				echo "Failed. $URL_RETURN"
				return 1
			}
			[ "$MD5" = $( md5sum $SERVER_LIST_FILE | grep -o '[0-9a-z]\{32\}' ) ] && {
				echo "Your server list was already up to date."
				return 2
			}
			URL_FILE=$( wget -qO - "$UPDATE_URL?password=${1}&md5=${MD5}" )
			[ "$URL_FILE" = "1" -o -z "$URL_FILE" ] && {
				echo "Failed to download list."
				return 1
			}
			URL_FILE=$( echo -e "$URL_FILE" | grep -o '.*|.*|.*|.*|.*|.*|.*' )
			[ -z "$URL_FILE" ] && {
				echo "Failed to update list."
				return 1	
			}
			echo -e "$URL_FILE">"$SERVER_LIST_FILE"
			echo "$( echo -e "$URL_FILE" | grep -c '\(.*\)' )</strong> servers has been updated."
			return 0
		fi
	fi
}

client_stop() {
	running_check && running_details
	[ -z "$RUNNING" ] && echo "Shadowsock client was not running." && return 1
	client_disable "$proxy_type_now"
	/etc/init.d/$proxy_type_now stop
	auto_switch_cron_remove
	echo "ShadowSocks ($RUNNING) stopped."
	return 0
}

client_restart() {
	running_check && running_details
	[ -z "$RUNNING" ] && echo "Shadowsock client was not running." && return 1
	[ -z "$1" ] || {
		[ "$1" = "$proxy_type_now" ] || {
			swtich_manual "$server_now" "$1" &>/dev/null && echo "Client switched to $1." && return 0
			echo "Fail to switch client to $1." && return 1
		}
	}
	/etc/init.d/$proxy_type_now restart
	echo "ShadowSocks ($RUNNING) restarted."
	return 0
}

client_enable() {
[ -z "$1" ] && return 1
	case $1 in
		redsocks2)
			[ -f "$REDSOCKS2_CONFIG" ] && {
				sed -Ei "s/option[ 	]+enabled.*/option enabled '1'/; s/option[ 	]+proxy_type.*/option proxy_type 'shadowsocks'/; s/option[ 	]+auto_proxy.*/option auto_proxy '1'/" "$REDSOCKS2_CONFIG"
			}
			;;
		shadowsocks)
			[ -f "$SHADOWSOCKS_CONFIG" ] && {
				sed -Ei "s/option[ 	]+enabled.*/option enabled '1'/" "$SHADOWSOCKS_CONFIG"
			}
			;;
	esac
}

client_disable() {
	[ -z "$1" ] && return 1
	case $1 in
		redsocks2)
			[ -f "$REDSOCKS2_CONFIG" ] && {
				sed -Ei "s/option[ 	]+enabled.*/option enabled '0'/" "$REDSOCKS2_CONFIG"
			}
			;;
		shadowsocks)
			[ -f "$SHADOWSOCKS_CONFIG" ] && {
				sed -Ei "s/option[ 	]+enabled.*/option enabled '0'/" "$SHADOWSOCKS_CONFIG"
			}
			;;
	esac
}

switch_auto() {
	[ -f "$SERVER_LIST_FILE" ] || {
		echo "No shadowsocks server in list." && return 1
	}
	all_servers=$( cat $SERVER_LIST_FILE )
	servers_auto=$( echo -e "$all_servers" | grep '|1$' | awk -F'|' '{ print $2 }' )
	[ -z "$servers_auto" ] && echo "No selected server for auto switch." && return 1
	running_check && running_details && {
		server_next=$( echo -e "$servers_auto" | grep -A1 "$server_now" )
		server_next=$( echo $server_next | awk '{print $2}' )
	}
	[ "$server_next" = "$server_now" -o -z "$server_next" -o -z "$server_now" ] && {
		server_next=$( echo $servers_auto | awk '{ print $1 }' )
	}
	if [ -z "$1" ]; then
		swtich_server "$server_next" "$proxy_type_now"
	else
		swtich_server "$server_next" "$1"
	fi
}

swtich_manual() {
	[ -z "$1" ] && echo "Please choose a server for switching." && return 1
	running_check && running_details && {
		[ "$1" = "$server_now" ] && {
			[ "$2" = "$proxy_type_now" ] && echo "$1 is already using." && return 1
		}
	}
	swtich_server "$1" "$2" && echo "Swithed server to $1." && return 0
	echo "Fail to switch server to $1." && return 1
}

swtich_server() {
	[ -z "$1" ] && return 1
	[ -z "$all_servers" ] && all_servers=$( cat $SERVER_LIST_FILE )
	[ -z "$all_servers" ] && return 1
	if [ -z "$2" ]; then
		proxy_type='shadowsocks'
	else
		proxy_type="$2"
	fi
	eval $( echo -e "$all_servers" | grep "$1" | awk -F'|' '{print "server_host="$2,"server_port="$3,"server_password="$4,"server_method="$5}')
	[ -z "$server_host" ] && echo "$1 was not in the server list." && return 1
	switch_config "$proxy_type"
	if [ -z "$RUNNING" ]; then
		client_enable "$proxy_type"
		/etc/init.d/$proxy_type enable &>/dev/null
		/etc/init.d/$proxy_type start &>/dev/null
	else
		if [ "$proxy_type" = "$proxy_type_now" ]; then
			/etc/init.d/$proxy_type_now switch "$process" "$option" &>/dev/null
		else
			client_disable "$proxy_type_now" &>/dev/null
			/etc/init.d/$proxy_type_now stop &>/dev/null
			client_enable "$proxy_type"
			/etc/init.d/$proxy_type enable &>/dev/null
			/etc/init.d/$proxy_type start &>/dev/null
		fi
	fi
	return 0
}

switch_config() {
[ -z "$1" ] && return 1
case $1 in
	redsocks2)
		[ -f "$REDSOCKS2_CONFIG" ] && {
			DATA_TMP=$(sed -E '/option[ 	]+proxy_type.*/d;/option[ 	]+shadowsocks_ip.*/d;/option[ 	]+shadowsocks_port.*/d;/option[ 	]+shadowsocks_password.*/d;/option[ 	]+shadowsocks_encrypt_method.*/d;' "$REDSOCKS2_CONFIG"|sed -E "s/(.*config[ 	]+redsocks2.*)/\1\n	option proxy_type 'shadowsocks'\n	option shadowsocks_ip '$server_host'\n	option shadowsocks_port '$server_port'\n	option shadowsocks_password '$server_password'\n	option shadowsocks_encrypt_method '$server_method'/")
			echo -e "$DATA_TMP">"$REDSOCKS2_CONFIG"
		}
		[ -f "$REDSOCKS2_CONF" ] && {
			sed -i -e "$(($line_no+2)),$(($line_no+4))d" -e "$(($line_no+1))s/.*/	ip = $server_host;\n	port = $server_port;\n	login = $server_method;\n	password = \"$server_password\";/" "$REDSOCKS2_CONF"
		}
		;;
	shadowsocks)
		[ -f "$SHADOWSOCKS_CONFIG" ] && {
			DATA_TMP=$(sed -E '/option[ 	]+server_host.*/d;/option[ 	]+server_port.*/d;/option[ 	]+password.*/d;/option[ 	]+encrypt_method.*/d;' "$SHADOWSOCKS_CONFIG"|sed -E "s/(.*config[ 	]+shadowsocks.*)/\1\n	option server_host '$server_host'\n	option server_port '$server_port'\n	option password '$server_password'\n	option encrypt_method '$server_method'/")
			echo -e "$DATA_TMP">"$SHADOWSOCKS_CONFIG"
		}
		[ -z "$option" ] || {
			option=$( echo $option | sed -E -e "s/-s[ ]+[^ ]+/-s $server_host/" -e "s/-p[ ]+[^ ]+/-p $server_port/" -e "s/-k[ ]+[^ ]+/-k $server_password/" -e "s/-m[ ]+[^ ]+/-m $server_method/" )
		}
		;;
esac
}

break_check() {
	break_check_bin
	case $? in
		0)
			echo "Break ($connect_type): true." && return 0
			;;
		1)
			echo "Break ($connect_type): false." && return 1
			;;
		2)
			echo "Sorry, no application for checking break." && return 2
			;;
	esac
}

break_check_bin() {
	connect_type=''
	command -v curl &>/dev/null && {
		connect_type='curl'
		curl -I -k --interface lo --retry $BREAK_CHECK_RETRY --connect-timeout $BREAK_CHECK_TIMEOUT "$BREAK_CHECK_URL" &>/dev/null && return 0
		return 1
	}
	wget --help | grep '\-\-no\-check\-certificate' &>/dev/null && {
		connect_type='wget'
		wget --tries=$BREAK_CHECK_RETRY --timeout=$BREAK_CHECK_TIMEOUT --server-response --spider --no-check-certificate "$BREAK_CHECK_URL" &>/dev/null && return 0
		return 1
	}
	return 2
}

auto_switch_cron_check() {
	USER=$(id -un)
	[ -z "$USER" ] || USER='root'
	CRON_FILE="/etc/crontabs/$USER"
	CRON_BIN=$( grep -m1 "$SS_WEB_BIN_NAME" "$CRON_FILE" )
	CRON_PID=$( pidof crond )
	[ -z "$CRON_BIN" -o -z "$CRON_PID" ] && return 1
	return 0
}

auto_switch_cron_set() {
	[ -z "$1" ] && return 1
	auto_switch_cron_check
	if [ -z "$CRON_BIN"  ]; then
		echo "*/$1 * * * * $SS_WEB_BIN auto_switch" >>$CRON_FILE
	else
		sed -i "s/.*$SS_WEB_BIN_NAME auto_switch.*/*\/$1 * * * * $(echo -e "$SS_WEB_BIN" | sed 's/\//\\\//g') auto_switch/" "$CRON_FILE"
	fi
	/etc/init.d/cron restart
}

auto_switch_cron_remove() {
	auto_switch_cron_check
	[ -z "$CRON_BIN" ] && return 1
	sed -i "/.*$SS_WEB_BIN_NAME auto_switch.*/d" $CRON_FILE
	if [ -z "$( cat $CRON_FILE )" ]; then
		rm -f $CRON_FILE
		/etc/init.d/cron stop
	else
		/etc/init.d/cron restart
	fi
	echo "Auto Switch has been disable."
	return 0
}

auto_switch_list() {
	[ -z "$1" ] && return 1
	DATA_auto=$( echo "$1" | tr ',' ' ' )
	list=$(
		while read LINE
		do
			LINE_IP=$( echo $LINE | awk -F'|' '{ print $2}' )
			LINE_IP=$( echo $DATA_auto | grep -o "$LINE_IP" )
			if [ -z "$LINE_IP" ]; then
				echo "${LINE%|*}|0"
			else
				echo "${LINE%|*}|1"	
			fi
		done  < $SERVER_LIST_FILE
		)
	echo -e "$list" >$SERVER_LIST_FILE
}

auto_switch() {
	while true
	do
		if break_check; then
			break
		else
			switch_auto
		fi
		[ "$starting_server" = "$server_next" ] && {
			auto_switch_cron_check && auto_switch_cron_set "15"
			echo "Please check your network connectivity."
			break
		}
		[ -z "$starting_server" ] && starting_server="$server_next"
	done
}

# Help Info
get_help() {
echo "Usage: ss-web command [options]

-b  break_check
-m  swtich_manual [*server ip] [*client: shadowsocks / redsocks2]
-a  switch_auto [client: shadowsocks / redsocks2]
-p  resolve_ip [*domain]
-l  get_location [*ip / domain]
-s  server_add [*server ip / domain] [*server port] [*password] [*method] [enable localtion] [enable auto]
    # eg. ss-web \"server_add 127.0.0.1,server.com\" 1080 password table 1 1
-r  server_remove [*server ip / domain]
-i  server_info [*server ip]
-u  server_update [password]
-S  client_stop
-R  client_restart [client: shadowsocks / redsocks2]
-c  auto_switch_cron_set [*check every ? minutes]
-d  auto_switch_cron_remove
-A  auto_switch
-h  --help
"
exit
}

CMD=''
case $1 in
	'-h')
		CMD='get_help'
		;;
	'--help')
		CMD='get_help'
		;;
	'-b')
		CMD='break_check'
		;;
	'-m')
		CMD='swtich_manual'
		;;
	'-a')
		CMD='switch_auto'
		;;
	'-p')
		CMD='resolve_ip'
		;;
	'-l')
		CMD='get_location'
		;;
	'-s')
		CMD='server_add'
		;;
	'-r')
		CMD='server_remove'
		;;
	'-i')
		CMD='server_info'
		;;
	'-u')
		CMD='server_update'
		;;
	'-S')
		CMD='client_stop'
		;;
	'-R')
		CMD='client_restart'
		;;
	'-c')
		CMD='auto_switch_cron_set'
		;;
	'-d')
		CMD='auto_switch_cron_remove'
		;;
	'-A')
		CMD='auto_switch'
		;;
esac

if [ -z "$CMD" ]; then
	eval "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
else
	eval "$CMD" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
fi
